package com.hyx.bookManager.utils;

import java.lang.Long;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @ClassName Snowflake
 * @Description TODO
 * @Author 黄乙轩
 * @Date 2021/6/6 22:25
 * @Version 1.0
 **/

@Component
public class Snowflake {

    public Snowflake(@Value("${snowFlake.workerId}") Long workerId,@Value("${snowFlake.datacenterId}") Long datacenterId,@Value("${snowFlake.sequence}") Long sequence) {
        //最大机器ID
        Long maxWorkerId = -1L ^ (-1L << workerIdBits);
        if (workerId > maxWorkerId) {
            throw new IllegalArgumentException(
                    String.format("机器ID不能超过%d和小于0", maxWorkerId)
            );
        }
        //最大机房ID
        Long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(
                    String.format("机房ID不能超过%d和小于0", maxDatacenterId)
            );
        }
        this.workId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }

    //机器ID
    private Long workId;
    //机房ID
    private Long datacenterId;
    //一毫秒生成的多个id的最新序号
    private Long sequence;
    //时间初始值
    private Long iniTime = 1585644268888L;
    //5位机器ID
    private Long workerIdBits = 5L;
    //5位机房ID
    private Long datacenterIdBits = 5L;
    //每毫秒产生id数的2的12次方
    private Long sequenceBits = 12L;

    private final Long workerIdShift = sequenceBits;
    private final Long datacenterIdShift = sequenceBits + workerIdBits;
    private final Long timeStampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private final Long sequenceMask = -1L ^ (-1L <<sequenceBits);
    //记录时间毫秒判断是否为同一毫秒
    private Long lastTimeStamp = -1L;

    //生成唯一ID
    public synchronized Long nextId() {
        Long timeStamp = System.currentTimeMillis();
        if (timeStamp < lastTimeStamp) {
            System.err.print("错误！，时间已被回调");
            throw new RuntimeException(
                    String.format("时间已被回调%d秒", lastTimeStamp - timeStamp)
            );
        }
        //如果在同一毫秒
        if (lastTimeStamp.equals(timeStamp)) {
            sequence = (sequence+1)&sequenceMask;
            if(sequence==0){
                timeStamp = tilNextMillis(lastTimeStamp);
            }
        }else{
            sequence = 0L;
        }
        lastTimeStamp = timeStamp;
        return ((timeStamp-iniTime)<<timeStampLeftShift)|(datacenterId<<datacenterIdShift)|(workId<<workerIdShift)|sequence;
    }

    private Long tilNextMillis(Long lastTimeStamp){
        Long now = System.currentTimeMillis();
        while(now<=lastTimeStamp){
            now=System.currentTimeMillis();
        }
        return now;
    }
}
